package com.javandad.app.server.db;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;

import javax.jdo.Query;

import com.javandad.app.server.entity.Fodo;
import com.javandad.app.shared.entity.Friendship;
import com.javandad.app.shared.entity.Membership;
import com.javandad.app.shared.entity.Message;
import com.javandad.app.shared.entity.User;
import com.javandad.app.shared.entity.UserGroup;
import com.javandad.app.shared.entity.home.HomePost;
import com.javandad.app.shared.entity.project.Post;
import com.javandad.app.shared.entity.project.Project;
import com.javandad.app.shared.entity.project.ProjectAssoc;

public class DatabaseManager {

	private static DatabaseManager defaultDatabaseManager = null;

	static {
		defaultDatabaseManager = new DatabaseManager();
	}

	public static DatabaseManager getDefaultDatabaseManager() {
		return defaultDatabaseManager;
	}

	public boolean isValid(String userId, PersistenceManager pm) {
		try {
			pm.getObjectById(User.class, userId);
			return true;
		} catch (JDOObjectNotFoundException e) {
			return false;
		}
	}

	private User prepareUserObject(User user, PersistenceManager pm) {
		Long id = null;
		String url = "/img/profile_default_pic.png";
		List<Fodo> fodos = getFodos(user.getUsername(), null, Fodo.PROFILE_FODO, pm);
		if (fodos != null && fodos.size() > 0) {
			id = fodos.get(0).getId();
			url = "/image?id=" + id;
		}
		user.setProfileFodo(id);
		user.setProfileFodoURL(url);

		return user;
	}

	public User getUser(String username, PersistenceManager pm) {
		List<User> users = (List<User>) getUsersByParameter("username", username, pm);
		if (!users.isEmpty())
			return users.get(0);
		else
			return null;
	}

	public List<User> getUsersByParameter(String parameter, String value, PersistenceManager pm) {
		Query query = pm.newQuery(User.class);
		String paramName = parameter + "Param";
		query.setFilter(parameter + " == " + paramName);
		query.declareParameters("String " + paramName);
		@SuppressWarnings("unchecked")
		List<User> result = (List<User>) query.execute(value);
		List<User> users = new ArrayList<User>();
		for (User user : result)
			users.add(prepareUserObject(user, pm));
		return users;

	}

	public List<UserGroup> getGroups(String username, PersistenceManager pm) {
		User user = this.getUser(username, pm);
		List<UserGroup> groups = new ArrayList<UserGroup>();
		for (Membership m : user.getGroupMemberships()) {
			UserGroup group = pm.getObjectById(UserGroup.class, m.getUserGroup());
			groups.add(group);
		}
		return groups;
	}

	private List<Message> getMessages(String user, boolean sender, PersistenceManager pm) {
		Query query = pm.newQuery(Message.class);
		String param = (sender ? "sender" : "receiver");
		query.setFilter(param + " == userParam");
		query.declareParameters("String userParam");
		List<Message> msgs = new ArrayList<Message>();
		@SuppressWarnings("unchecked")
		List<Message> result = (List<Message>) query.execute(user);
		msgs.addAll(result);
		return msgs;
	}

	public List<Message> getMessages(String sender, String receiver, PersistenceManager pm) {
		if (sender == null)
			return getMessages(receiver, false, pm);
		else if (receiver == null)
			return getMessages(sender, true, pm);
		Query query = pm.newQuery(Message.class);
		query.setFilter("sender == senderParam, receiver == receiverParam");
		query.declareParameters("String senderParam, String receiverParam");
		List<Message> msgs = new ArrayList<Message>();
		@SuppressWarnings("unchecked")
		List<Message> result = (List<Message>) query.execute(sender, receiver);
		msgs.addAll(result);
		return msgs;
	}

	public List<User> getFriends(String username, boolean invited, PersistenceManager pm) {
		Query query = pm.newQuery(Friendship.class);
		String field = (invited ? "inviter" : "confirmer");
		query.setFilter(field + " == username && dateOfConfirmation != null");
		query.declareParameters("String username");
		List<User> friends = new ArrayList<User>();
		@SuppressWarnings("unchecked")
		List<Friendship> result = (List<Friendship>) query.execute(username);
		for (Friendship f : result) {
			String friendID = (invited ? f.getConfirmer() : f.getInviter());
			User friend = getUser(friendID, pm);
			friends.add(friend);
		}
		return friends;
	}

	public List<User> getFriends(String username, PersistenceManager pm) {
		List<User> friends = new ArrayList<User>();
		friends.addAll(getFriends(username, true, pm));
		friends.addAll(getFriends(username, false, pm));
		return friends;
	}

	public List<Friendship> getInvitations(String receiver, PersistenceManager pm) {
		Query query = pm.newQuery(Friendship.class);
		query.setFilter("confirmer == username && dateOfConfirmation == null");
		query.declareParameters("String username");
		List<Friendship> invitations = new ArrayList<Friendship>();
		@SuppressWarnings("unchecked")
		List<Friendship> result = (List<Friendship>) query.execute(receiver);
		for (Friendship invi : result) {
			invitations.add(invi);
		}
		return invitations;
	}

	public Friendship getFriendship(String user1, String user2, PersistenceManager pm) {
		Query query = pm.newQuery(Friendship.class);
		query.setFilter("(inviter == user1 || inviter == user2) && (confirmer == user1 || confirmer == user2)");
		query.declareParameters("String user1, String user2");
		@SuppressWarnings("unchecked")
		List<Friendship> friends = (List<Friendship>) query.execute(user1, user2);
		if (friends.isEmpty())
			return null;
		else
			return friends.get(0);
	}

	public List<User> getMembers(UserGroup group, PersistenceManager pm) {
		Query query = pm.newQuery(Membership.class);
		query.setFilter("userGroup == groupUrl");
		query.declareParameters("String groupUrl");
		List<User> members = new ArrayList<User>();
		@SuppressWarnings("unchecked")
		List<Membership> result = (List<Membership>) query.execute(group.getGroupURL());
		for (Membership m : result) {
			User member = m.getMember();
			member.getFirstName(); // necessary so the User object actually gets filled...
			members.add(member);
		}
		return members;
	}

	public List<User> getAssociatedUsers(String project, int level, PersistenceManager pm) {
		List<User> users = new ArrayList<User>();
		Query query = pm.newQuery(ProjectAssoc.class);
		query.setFilter("projectname == projectParam");
		query.declareParameters("String projectParam");
		@SuppressWarnings("unchecked")
		List<ProjectAssoc> result = (List<ProjectAssoc>) query.execute(project);
		for (ProjectAssoc a : result) {
			if ((a.getLevel() | level) == a.getLevel() && (a.getDateBegin() != null || (level | ProjectAssoc.PARTICIPATING_PROJECT) != level)) {
				User user = getUser(a.getUsername(), pm);
				users.add(user);
			}

		}
		return users;
	}

	public List<Project> getAssociatedProjects(String user, int level, PersistenceManager pm) {
		List<Project> projects = new ArrayList<Project>();
		Query query = pm.newQuery(ProjectAssoc.class);
		query.setFilter("username == userParam");
		query.declareParameters("String userParam");
		@SuppressWarnings("unchecked")
		List<ProjectAssoc> result = (List<ProjectAssoc>) query.execute(user);
		for (ProjectAssoc a : result) {

			if ((a.getLevel() | level) == a.getLevel()) {
				// don't include unconfirmed participation invitations
				if (a.getDateBegin() != null || (level | ProjectAssoc.PARTICIPATING_PROJECT) != level) {
					Project project = pm.getObjectById(Project.class, a.getProjectname());

					projects.add(project);
				}
			}
		}
		return projects;
	}

	public List<ProjectAssoc> getProjectAssociations(String user, int level, PersistenceManager pm) {
		List<ProjectAssoc> projects = new ArrayList<ProjectAssoc>();
		Query query = pm.newQuery(ProjectAssoc.class);
		query.setFilter("username == userParam");
		query.declareParameters("String userParam");
		@SuppressWarnings("unchecked")
		List<ProjectAssoc> res = (List<ProjectAssoc>) query.execute(user);
		for (ProjectAssoc a : res) {

			if ((a.getLevel() | level) == a.getLevel()) {
				// don't include unconfirmed participation invitations
				if (a.getDateBegin() != null || (level | ProjectAssoc.PARTICIPATING_PROJECT) != level) {
					projects.add(a);
				}
			}
		}
		return projects;
	}

	public ProjectAssoc getAssociation(String projectname, String username, PersistenceManager pm) {
		ProjectAssoc pa = null;
		if (projectname != null && username != null) {

			Query query = pm.newQuery(ProjectAssoc.class);
			query.setFilter("projectname == projectParam && username == userParam ");
			query.declareParameters("String projectParam, String userParam");
			@SuppressWarnings("unchecked")
			List<ProjectAssoc> list = (List<ProjectAssoc>) query.execute(projectname, username);
			if (list != null && !list.isEmpty())
				pa = list.get(0);
		}
		return pa;
	}

	public List<Post> getProjectPosts(String projectname, String username, int level, int count, PersistenceManager pm) {
		List<Post> posts = null;

		if (projectname != null) {
			posts = new LinkedList<Post>();

			Query query = pm.newQuery("select from " + Post.class.getName() + " order by date desc");
			query.setFilter("projectname == projectParam");
			query.declareParameters("String projectParam");

			@SuppressWarnings("unchecked")
			List<Post> result = (List<Post>) query.execute(projectname);
			for (Post post : result) {

				post.setUser(getUser(post.getUsername(), pm));
				int rl = ProjectAssoc.NOASSOC;
				String u = post.getUsername();
				if (u != null && !u.equalsIgnoreCase("Anonymous")) {
					ProjectAssoc pa = getAssociation(projectname, u, pm);
					if (pa != null) {
						rl = pa.getLevel();
						if (((rl | ProjectAssoc.PARTICIPATING_PROJECT) == rl) && pa.getDateBegin() == null)
							rl = rl ^ ProjectAssoc.PARTICIPATING_PROJECT;
					}
				}

				if ((level | rl) == rl || (level | rl) == level)
					posts.add(post);
			}
		}
		return posts;
	}

	public List<ProjectAssoc> getParticipationInvitations(String username, PersistenceManager pm) {
		Query query = pm.newQuery(ProjectAssoc.class);
		query.setFilter("username == userParam && level == levelParam && dateBegin == null");
		query.declareParameters("String userParam, int levelParam");
		List<ProjectAssoc> invitations = new ArrayList<ProjectAssoc>();
		@SuppressWarnings("unchecked")
		List<ProjectAssoc> result = (List<ProjectAssoc>) query.execute(username, ProjectAssoc.PARTICIPATING_PROJECT | ProjectAssoc.FOLLOWING_PROJECT);
		for (ProjectAssoc invi : result) {
			invitations.add(invi);
		}
		return invitations;
	}

	public List<Project> getProjects(PersistenceManager pm) {
		Query query = pm.newQuery(Project.class);
		@SuppressWarnings("unchecked")
		List<Project> result = (List<Project>) query.execute();
		return result;

	}

	public List<User> getUsers(PersistenceManager pm) {
		Query query = pm.newQuery(User.class);
		@SuppressWarnings("unchecked")
		List<User> result = (List<User>) query.execute();
		return result;

	}

	public List<UserGroup> getUserGroups(PersistenceManager pm) {
		Query query = pm.newQuery(UserGroup.class);
		@SuppressWarnings("unchecked")
		List<UserGroup> result = (List<UserGroup>) query.execute();
		return result;

	}

	public List<Fodo> getFodos(String username, String directory, String type, PersistenceManager pm) {
		Query query = pm.newQuery(Fodo.class);
		query.setFilter("directory == directoryParam && user==userParam && type == typeParam");
		query.declareParameters("String directoryParam, String userParam, String typeParam");
		@SuppressWarnings("unchecked")
		List<Fodo> fodos = (List<Fodo>) query.execute(directory, username, type);
		return fodos;
	}

	public List<Fodo> getFodos(String username, String directory, PersistenceManager pm) {
		Query query = pm.newQuery(Fodo.class);
		query.setFilter("directory == directoryParam && user==userParam");
		query.declareParameters("String directoryParam, String userParam");
		@SuppressWarnings("unchecked")
		List<Fodo> fodos = (List<Fodo>) query.execute(directory, username);
		return fodos;
	}

	public List<Fodo> getFodos(PersistenceManager pm) {
		Query query = pm.newQuery(Fodo.class);
		@SuppressWarnings("unchecked")
		List<Fodo> fodos = (List<Fodo>) query.execute();
		return fodos;
	}

	public Fodo getFodo(String username, long id, PersistenceManager pm) {
		try {
			Fodo fodo = pm.getObjectById(Fodo.class, id);
			if (username != null && !fodo.getUser().equals(username))
				return null;
			return fodo;
		} catch (JDOObjectNotFoundException e) {
			return null;
		}
	}

	public Fodo getFodo(String type, PersistenceManager pm) {
		Query query = pm.newQuery(Fodo.class);
		query.setFilter("type == typeParam");
		query.declareParameters("String typeParam");
		@SuppressWarnings("unchecked")
		List<Fodo> fodos = (List<Fodo>) query.execute(type);
		return ((fodos != null && fodos.size() > 0) ? fodos.get(0) : null);
	}

	public List<HomePost> getHomePosts(String username, String[] friends, PersistenceManager pm) {
		Query query = pm.newQuery("select from " + HomePost.class.getName() + " order by dateOfPost desc");
		query.setFilter("posted == userParam");
		query.declareParameters("String userParam");
		@SuppressWarnings("unchecked")
		List<HomePost> posts = (List<HomePost>) query.execute(username);
		for (HomePost post : posts) {
			post.setPosterObj(DatabaseManager.getDefaultDatabaseManager().getUser(post.getPoster(), pm));
		}
		return posts;

	}

}